package com.ibeeking.found.job.service.impl;

import com.ibeeking.found.job.annotation.EnableJob;
import com.ibeeking.gmc.job.core.biz.AdminBiz;
import com.ibeeking.gmc.job.core.biz.model.BaseJobParam;
import com.ibeeking.gmc.job.core.biz.model.JobInfoParam;
import com.ibeeking.gmc.job.core.biz.model.JobStartStopParam;
import com.ibeeking.gmc.job.core.biz.model.ReturnT;
import com.ibeeking.gmc.job.core.executor.XxlJobExecutor;
import com.ibeeking.found.job.model.BaseJobModel;
import com.ibeeking.found.job.model.BusinessKeyModel;
import com.ibeeking.found.job.model.JobInfoModel;
import com.ibeeking.found.job.model.JobStartStopModel;
import com.ibeeking.found.job.service.IJobService;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.utils.bean.BeanUtil;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.data.ParamCheckUtils;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.json.JsonUtils;
import com.ibeeking.nematos.utils.result.ResponseCodeEnum;
import com.ibeeking.nematos.utils.result.ResponseResult;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.quartz.CronExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;

/**
 * @ClassName JobServiceImpl
 * @Description
 * @Author ibeeking
 * @Date 2021-01-31 10:51
 **/
@ConditionalOnBean(annotation = EnableJob.class)
@Service
public class JobServiceImpl implements IJobService {

    @Resource
    private XxlJobExecutor xxlJobSpringExecutor;

    @Async("callerRunsPolicy")
    @Override
    public Future<ResponseResult<Boolean>> createJob(JobInfoModel model) {
        AdminBiz adminBiz = null;
        try {
            ParamCheckUtils.check(model);
            boolean cron = CronExpression.isValidExpression(model.getJobCron());
            if (!cron) {
                throw new BusinessException("cron表达式错误");
            }
            JobInfoParam jobInfoParam = BeanUtil.convertBean(model, JobInfoParam.class);
            jobInfoParam.setAppName(StringUtils.isEmpty(model.getAppName())?xxlJobSpringExecutor.getAppname():model.getAppName());
            BusinessKeyModel businessKeyModel = new BusinessKeyModel();
            businessKeyModel.setBusinessKey(model.getBusinessKey());
            String addJson = JsonUtils.toJsonStr(businessKeyModel);
            if (StringUtils.isBlank(jobInfoParam.getExecutorParam())) {
                jobInfoParam.setExecutorParam(addJson);
            } else {
                JSONObject jsonObject = JSON.parseObject(jobInfoParam.getExecutorParam());
                jsonObject.putAll(JSON.parseObject(addJson, Map.class));
                jobInfoParam.setExecutorParam(jsonObject.toJSONString());
            }
            adminBiz = adminBiz();
            ReturnT<String> job = adminBiz.createJob(jobInfoParam);
            if (HttpStatus.OK.value() != job.getCode()) {
                throw new BusinessException(job.getMsg());
            }
            return new AsyncResult<>(ResponseResult.success(true));
        } catch (Exception e) {
            return new AsyncResult<>(ResponseResult.fail(ResponseCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage(), false));
        }
    }

    @Async("callerRunsPolicy")
    @Override
    public Future<ResponseResult<Boolean>> modifyJob(JobInfoModel model) {
        AdminBiz adminBiz = null;
        try {
            ParamCheckUtils.check(model);
            boolean cron = CronExpression.isValidExpression(model.getJobCron());
            if (!cron) {
                throw new BusinessException("cron表达式错误");
            }
            JobInfoParam jobInfoParam = BeanUtil.convertBean(model, JobInfoParam.class);
            jobInfoParam.setAppName(StringUtils.isEmpty(model.getAppName())?xxlJobSpringExecutor.getAppname():model.getAppName());
            BusinessKeyModel businessKeyModel = new BusinessKeyModel();
            businessKeyModel.setBusinessKey(model.getBusinessKey());
            String addJson = JsonUtils.toJsonStr(businessKeyModel);
            if (StringUtils.isBlank(jobInfoParam.getExecutorParam())) {
                jobInfoParam.setExecutorParam(addJson);
            } else {
                JSONObject jsonObject = JSON.parseObject(jobInfoParam.getExecutorParam());
                jsonObject.putAll(JSON.parseObject(addJson, Map.class));
                jobInfoParam.setExecutorParam(jsonObject.toJSONString());
            }
            adminBiz = adminBiz();
            ReturnT<String> job = adminBiz.modifyJob(jobInfoParam);
            if (HttpStatus.OK.value() != job.getCode()) {
                throw new BusinessException(job.getMsg());
            }
            return new AsyncResult<>(ResponseResult.success(true));
        } catch (Exception e) {
            return new AsyncResult<>(ResponseResult.fail(ResponseCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage(), false));
        }
    }

    @Async("callerRunsPolicy")
    @Override
    public Future<ResponseResult<Boolean>> startStopJob(JobStartStopModel model) {
        AdminBiz adminBiz = null;
        try {
            ParamCheckUtils.check(model);
            JobStartStopParam param = BeanUtil.convertBean(model, JobStartStopParam.class);
            param.setAppName(StringUtils.isEmpty(model.getAppName())?xxlJobSpringExecutor.getAppname():model.getAppName());
            adminBiz = adminBiz();
            ReturnT<String> job = adminBiz.startStopJob(param);
            if (HttpStatus.OK.value() != job.getCode()) {
                throw new BusinessException(job.getMsg());
            }
            return new AsyncResult<>(ResponseResult.success(true));
        } catch (Exception e) {
            return new AsyncResult<>(ResponseResult.fail(ResponseCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage(), false));
        }
    }

    @Async("callerRunsPolicy")
    @Override
    public Future<ResponseResult<Boolean>> executeOnceJob(BaseJobModel model) {
        AdminBiz adminBiz = null;
        try {
            ParamCheckUtils.check(model);
            BaseJobParam baseJobParam = BeanUtil.convertBean(model, BaseJobParam.class);
            baseJobParam.setAppName(StringUtils.isEmpty(model.getAppName())?xxlJobSpringExecutor.getAppname():model.getAppName());
            adminBiz = adminBiz();
            ReturnT<String> job = adminBiz.executeOnceJob(baseJobParam);
            if (HttpStatus.OK.value() != job.getCode()) {
                throw new BusinessException(job.getMsg());
            }
            return new AsyncResult<>(ResponseResult.success(true));
        } catch (Exception e) {
            return new AsyncResult<>(ResponseResult.fail(ResponseCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage(), false));
        }
    }

    @Async("callerRunsPolicy")
    @Override
    public Future<ResponseResult<List<String>>> nextJobTime(BaseJobModel model) {
        AdminBiz adminBiz = null;
        try {
            ParamCheckUtils.check(model);
            BaseJobParam baseJobParam = BeanUtil.convertBean(model, BaseJobParam.class);
            baseJobParam.setAppName(StringUtils.isEmpty(model.getAppName())?xxlJobSpringExecutor.getAppname():model.getAppName());
            adminBiz = adminBiz();
            ReturnT<String> job = adminBiz.nextJobTime(baseJobParam);
            if (HttpStatus.OK.value() != job.getCode()) {
                throw new BusinessException(job.getMsg());
            }
            return new AsyncResult<>(ResponseResult.success(Arrays.asList(job.getContent().split(","))));
        } catch (Exception e) {
            return new AsyncResult<>(ResponseResult.fail(ResponseCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage(), Collections3.emptyList()));
        }
    }

    @Async("callerRunsPolicy")
    @Override
    public Future<ResponseResult<Boolean>> delJob(BaseJobModel model) {
        AdminBiz adminBiz = null;
        try {
            ParamCheckUtils.check(model);
            BaseJobParam baseJobParam = BeanUtil.convertBean(model, BaseJobParam.class);
            baseJobParam.setAppName(StringUtils.isEmpty(model.getAppName())?xxlJobSpringExecutor.getAppname():model.getAppName());
            adminBiz = adminBiz();
            ReturnT<String> job = adminBiz.delJob(baseJobParam);
            if (HttpStatus.OK.value() != job.getCode()) {
                throw new BusinessException(job.getMsg());
            }
            return new AsyncResult<>(ResponseResult.success(true));
        } catch (Exception e) {
            return new AsyncResult<>(ResponseResult.fail(ResponseCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage(), false));
        }
    }

    private AdminBiz adminBiz() {
        List<AdminBiz> adminBizList = XxlJobExecutor.getAdminBizList();
        if (!Collections3.isEmpty(adminBizList)) {
            return adminBizList.get(0);
        } else {
            throw new BusinessException("任务服务未启动....");
        }
    }
}
